//||=========================================================================||
//||                                                                         ||
//||                         CudaCounterTemplate.cu                          ||
//||                                                                         ||
//||                           by the_best_goose))                           ||
//||                                                                         ||
//||=========================================================================||

#include <assert.h>
#include <stdio.h>
#include "CudaFastComplex.h"
#include "CudaFastComplex.cpp"

//-----------------------------------------------------------------------------

typedef float TPoint;

#define BLOCK_SIZE 10

#define C c
#define Z z
#define T t

//-----------------------------------------------------------------------------

__global__ void CountMandelbrotKernel(FastComplex t, FastComplex* iter_points,
                                      int iter_points_count,
                                      TPoint left,   TPoint right, 
                                      TPoint bottom, TPoint top,
                                      int iters_count, int* result_arr, 
                                      int size_w, int size_h);
__device__ void Iterate(FastComplex& z, FastComplex& c,FastComplex& t);

//-----------------------------------------------------------------------------

extern "C" void CountMandelbrot(FastComplex t, FastComplex* iter_points,
                                int iter_points_count,
                                TPoint left,   TPoint right, 
                                TPoint bottom, TPoint top,
                                int iters_count, int* result_arr, 
                                int size_w, int size_h)
{
    assert(result_arr);
    assert(iter_points);
    int* result_arr_kern = NULL;
    int size = size_w * size_h * sizeof(int);

    cudaMalloc((void**)&result_arr_kern, size);
    assert(result_arr_kern != NULL);

    FastComplex* iter_points_kern = NULL;
    cudaMalloc((void**)&iter_points_kern,
               iter_points_count * sizeof(FastComplex));
    cudaMemcpy(iter_points_kern, iter_points,
               iter_points_count * sizeof(FastComplex), cudaMemcpyHostToDevice);
    assert(iter_points_kern != NULL);

    dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
    dim3 dimGrid(size_w / dimBlock.x, size_h / dimBlock.y);

    for(int i = 0; i < size_w * size_h; ++i)
        result_arr [ i ] = 0;
    CountMandelbrotKernel <<<dimGrid, dimBlock>>>(t, iter_points_kern,
                                                  iter_points_count,
                                                  left, right, bottom, top,
                                                  iters_count, result_arr_kern,
                                                  size_w, size_h);
 

    cudaMemcpy(result_arr, result_arr_kern, size, cudaMemcpyDeviceToHost);
    cudaFree(result_arr_kern);
    cudaFree(iter_points_kern);
}

//-----------------------------------------------------------------------------
    
__global__ void CountMandelbrotKernel(FastComplex t, FastComplex* iter_points,
                                      int iter_points_count,
                                      TPoint left,   TPoint right, 
                                      TPoint bottom, TPoint top,
                                      int iters_count, int* result_arr, 
                                      int size_w, int size_h)

    {
    int x_result = blockIdx.x * BLOCK_SIZE + threadIdx.x;
    int y_result = size_h - 1 - (blockIdx.y * BLOCK_SIZE + threadIdx.y);

    int iters_max = 0;
    int iters = 0;
    int k = 0;

    FastComplex z;
    FastComplex c;
    
    for(k = 0; k < iter_points_count; ++k)
    {
        z.re = iter_points[k].re;
        z.im = iter_points[k].im;

        c.re = x_result * ( right - left ) / size_w + left;
        c.im = y_result * ( top - bottom ) / size_h + bottom;

        for(iters = 0; iters < iters_count; ++iters )
            {
            Iterate(z, c, t);
            if(z.re * z.re + z.im * z.im > 1000)
                break;
            }
        if(iters > iters_max)
            iters_max = iters;
    }

    result_arr[y_result * size_w + x_result] = iters_max;
    }

//-----------------------------------------------------------------------------


